package my.mypool.core.memcache;

import my.mypool.core.PoolablePool;
import com.mtbu.comm.PropUtil;
import my.tool.ConfigProp;

import java.net.Socket;
import java.net.URLEncoder;
import java.net.URLDecoder;
import java.io.*;
import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.LinkedList;

public class MemCacheClient {
    private PoolablePool pool = null;
    private static final String MEMSPLIT_STR = PropUtil.getInstance("memcache").getProperty("my.memcache.split", "MEMSPLIT");
    private static final String MEMDELPREFIX_STR = PropUtil.getInstance("memcache").getProperty("my.memcache.delprefix", "MEMDEL");
    private static DeletePolicy policy = null;

    static {
        try {
            Class clazz = Class.forName( ConfigProp.getProperties("my.memcache.delpolicy"));
            policy = (DeletePolicy) clazz.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public MemCacheClient(String host, int port,int maxIdle, int initIdleCapacity) throws Exception {
        pool = new PoolablePool(PoolableSocket.class, host + ":" + port,maxIdle,initIdleCapacity);
    }

    public String getSPLITSTR() {
        return MEMSPLIT_STR;
    }

    public String getDELPREFIXSTR() {
        return MEMDELPREFIX_STR;
    }

    public String encodeValue(String value) throws Exception {
           if(value==null) return null;
           return URLEncoder.encode(value,"utf-8");
    }

    public String decodeValue(String value) throws Exception {
           if(value==null) return null;
           return URLDecoder.decode(value,"utf-8");
    }

    public String get(String key) throws Exception {
        key=encodeValue(key);
        if (key == null || key.equals("")) throw new Exception("the key is null,key=" + key);
        PoolableSocket poolableSocket = pool.borrowObject();
        try {
            Socket socket = poolableSocket.getSocket();
            BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            bw.write("get " + key + "\r\n");
            bw.flush();
            String line = br.readLine();
            while (line != null) {
                if (line.equals("END")) {
                    break;
                } else if (line.startsWith("VALUE")) {
                    String value = br.readLine();
                    if (value.endsWith(MEMSPLIT_STR)) {
                        value = value.substring(0, value.length()-MEMSPLIT_STR.length());
                    }
                    return decodeValue(value);
                } else if (line.startsWith("ERROR")) {
                    throw new Exception("MemCache_Result_ERROR");
                } else if (line.startsWith("CLIENT_ERROR")) {
                    throw new Exception("MemCache_Result_CLIENT_ERROR");
                } else if (line.startsWith("SERVER_ERROR ")) {
                    throw new Exception("MemCache_Result_SERVER_ERROR ");
                } else {
                    throw new Exception("MemCache_Result_UNKNOWN_ERROR");
                }
            }
        } catch (Exception ex) {
            throw ex;
        } finally {
            pool.returnObject(poolableSocket);
        }
        return null;
    }

    public List<String> get(String key, long expire) throws Exception {
        String value = get(key);
        if (value == null) {
            return null;
        }
        List<String> records = new LinkedList<String>();
        String[] rds = value.split(MEMSPLIT_STR);
        if (policy == null) {
            for (int i = 0; i < rds.length; i++) {
                if (rds[i] == null || rds[i].trim().equals("")) continue;
                if (rds[i].startsWith(MEMDELPREFIX_STR)) continue;
                records.add(rds[i]);
            }
            return records;
        } else {
        	
            List<String[]> delRds = new LinkedList<String[]>();
            for (int i = 0; i < rds.length; i++) {
                if (rds[i] == null || rds[i].trim().equals("")) continue;
                if (rds[i].startsWith(MEMDELPREFIX_STR)) {
                    delRds.add(new String[]{"" + i, rds[i].substring(MEMDELPREFIX_STR.length())});
                }
            }
            for (int i = 0; i < rds.length; i++) {
                if (rds[i] == null || rds[i].trim().equals("")) continue;
                if (rds[i].startsWith(MEMDELPREFIX_STR)) continue;
                boolean isRdDel = false;
                for (int j = 0; j < delRds.size(); j++) {
                    String[] delrd = delRds.get(j);
                    int delIndex = Integer.parseInt(delrd[0]);
                    String delvalue = delrd[1];
                    if (i < delIndex && policy.isDeletable(rds[i], delvalue)) {
                        isRdDel = true;
                        break;
                    }
                }
                if (!isRdDel) {
                    records.add(rds[i]);
                }
            }
            if (records.size() < rds.length) {
                String values = "";
                for (int i = 0; i < records.size(); i++) {
                    values += records.get(i) + MEMSPLIT_STR;
                }
                set(key, values, expire);
            }
            return records;
        }
    }

    public Map<String, String> get(String[] keys) throws Exception {
        if (keys == null || keys.length <= 0) throw new Exception("the size of keys is 0");
        String key = "";
        for (int i = 0; i < keys.length; i++) {
            keys[i]=encodeValue(keys[i]);
            if (keys[i] == null)
                throw new Exception("uninvalid key:" + keys[i]);
            if (!key.equals("")) key += " ";
            key += keys[i];
        }
        Map<String, String> map = new HashMap<String, String>();
        PoolableSocket poolableSocket = pool.borrowObject();
        try {
            Socket socket = poolableSocket.getSocket();
            BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            bw.write("get " + key + "\r\n");
            bw.flush();
            String line = br.readLine();
            while (line != null) {
                if (line.equals("END")) {
                    break;
                } else if (line.startsWith("VALUE")) {
                    String subkey = line.split(" ")[1];
                    map.put(decodeValue(subkey),decodeValue(br.readLine()));
                    line = br.readLine();
                } else if (line.startsWith("ERROR")) {
                    throw new Exception("MemCache_Result_ERROR");
                } else if (line.startsWith("CLIENT_ERROR")) {
                    throw new Exception("MemCache_Result_CLIENT_ERROR");
                } else if (line.startsWith("SERVER_ERROR ")) {
                    throw new Exception("MemCache_Result_SERVER_ERROR ");
                } else {
                    throw new Exception("MemCache_Result_UNKNOWN_ERROR");
                }
            }

        } catch (Exception ex) {
            throw ex;
        } finally {
            pool.returnObject(poolableSocket);
        }
        return map;
    }

    public boolean set(String key, String value, long expire) throws Exception {
        key=encodeValue(key);
        value=encodeValue(value);
        if (key == null || key.equals("")) throw new Exception("the key is null,key=" + key);
        if (value == null) throw new Exception("the value is null,value=" + value);
        PoolableSocket poolableSocket = pool.borrowObject();
        try {
            Socket socket = poolableSocket.getSocket();
            BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            bw.write("set " + key + " 0 " + expire + " " + (value + MEMSPLIT_STR).getBytes().length + "\r\n");
            bw.write(value + MEMSPLIT_STR);
            bw.write("\r\n");
            bw.flush();
            String line = br.readLine();
            while (line != null) {
                if (line.equals("STORED")) {
                    return true;
                } else if (line.equals("NOT_STORED")) {
                    return false;
                } else if (line.startsWith("ERROR")) {
                    throw new Exception("MemCache_Result_ERROR");
                } else if (line.startsWith("CLIENT_ERROR")) {
                    throw new Exception("MemCache_Result_CLIENT_ERROR");
                } else if (line.startsWith("SERVER_ERROR ")) {
                    throw new Exception("MemCache_Result_SERVER_ERROR ");
                } else {
                    throw new Exception("MemCache_Result_UNKNOWN_ERROR");
                }
            }
        } catch (Exception ex) {
            throw ex;
        } finally {
            pool.returnObject(poolableSocket);
        }
       throw new Exception("MemCache_Result_NORESPONSE_ERROR");
    }

    public boolean add(String key, String value, long expire) throws Exception {
        key=encodeValue(key);
        value=encodeValue(value);
        if (key == null || key.equals("")) throw new Exception("the key is null,key=" + key);
        if (value == null) throw new Exception("the value is null,value=" + value);
        PoolableSocket poolableSocket = pool.borrowObject();
        try {
            Socket socket = poolableSocket.getSocket();
            BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            bw.write("add " + key + " 0 " + expire + " " + (value + MEMSPLIT_STR).getBytes().length + "\r\n");
            bw.write(value + MEMSPLIT_STR);
            bw.write("\r\n");
            bw.flush();
            String line = br.readLine();
            while (line != null) {
                if (line.equals("STORED")) {
                    return true;
                } else if (line.equals("NOT_STORED")) {
                    return false;
                } else if (line.startsWith("ERROR")) {
                    throw new Exception("MemCache_Result_ERROR");
                } else if (line.startsWith("CLIENT_ERROR")) {
                    throw new Exception("MemCache_Result_CLIENT_ERROR");
                } else if (line.startsWith("SERVER_ERROR ")) {
                    throw new Exception("MemCache_Result_SERVER_ERROR ");
                } else {
                    throw new Exception("MemCache_Result_UNKNOWN_ERROR");
                }
            }
        } catch (Exception ex) {
            throw ex;
        } finally {
            pool.returnObject(poolableSocket);
        }
        throw new Exception("MemCache_Result_NORESPONSE_ERROR");
    }

    public boolean replace(String key, String value, long expire) throws Exception {
        key=encodeValue(key);
        value=encodeValue(value);
        if (key == null || key.equals("")) throw new Exception("the key is null,key=" + key);
        if (value == null) throw new Exception("the value is null,value=" + value);
        PoolableSocket poolableSocket = pool.borrowObject();
        try {
            Socket socket = poolableSocket.getSocket();
            BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            bw.write("replace " + key + " 0 " + expire + " " + (value + MEMSPLIT_STR).getBytes().length + "\r\n");
            bw.write(value + MEMSPLIT_STR);
            bw.write("\r\n");
            bw.flush();
            String line = br.readLine();
            while (line != null) {
                if (line.equals("STORED")) {
                    return true;
                } else if (line.equals("NOT_STORED")) {
                    return false;
                } else if (line.startsWith("ERROR")) {
                    throw new Exception("MemCache_Result_ERROR");
                } else if (line.startsWith("CLIENT_ERROR")) {
                    throw new Exception("MemCache_Result_CLIENT_ERROR");
                } else if (line.startsWith("SERVER_ERROR ")) {
                    throw new Exception("MemCache_Result_SERVER_ERROR ");
                } else {
                    throw new Exception("MemCache_Result_UNKNOWN_ERROR");
                }
            }
        } catch (Exception ex) {
            throw ex;
        } finally {
            pool.returnObject(poolableSocket);
        }
       throw new Exception("MemCache_Result_NORESPONSE_ERROR");
    }

    public boolean append(String key, String value, long expire) throws Exception {
        key=encodeValue(key);
        value=encodeValue(value);
        if (key == null || key.equals("")) throw new Exception("the key is null,key=" + key);
        if (value == null) throw new Exception("the value is null,value=" + value);
        PoolableSocket poolableSocket = pool.borrowObject();
        try {
            Socket socket = poolableSocket.getSocket();
            BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            bw.write("append " + key + " 0 " + expire + " " + (value + MEMSPLIT_STR).getBytes().length + "\r\n");
            bw.write(value + MEMSPLIT_STR);
            bw.write("\r\n");
            bw.flush();
            String line = br.readLine();
            while (line != null) {
                if (line.equals("STORED")) {
                    return true;
                } else if (line.equals("NOT_STORED")) {
                    return set(key, value, expire);
                } else if (line.startsWith("ERROR")) {
                    throw new Exception("MemCache_Result_ERROR");
                } else if (line.startsWith("CLIENT_ERROR")) {
                    throw new Exception("MemCache_Result_CLIENT_ERROR");
                } else if (line.startsWith("SERVER_ERROR ")) {
                    throw new Exception("MemCache_Result_SERVER_ERROR ");
                } else {
                    throw new Exception("MemCache_Result_UNKNOWN_ERROR");
                }
            }
        } catch (Exception ex) {
            throw ex;
        } finally {
            pool.returnObject(poolableSocket);
        }
        throw new Exception("MemCache_Result_NORESPONSE_ERROR");
    }

    public boolean preappend(String key, String value, long expire) throws Exception {
        key=encodeValue(key);
        value=encodeValue(value);
        if (key == null || key.equals("")) throw new Exception("the key is null,key=" + key);
        if (value == null) throw new Exception("the value is null,value=" + value);
        PoolableSocket poolableSocket = pool.borrowObject();
        try {
            Socket socket = poolableSocket.getSocket();
            BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            bw.write("prepend " + key + " 0 " + expire + " " + (value + MEMSPLIT_STR).getBytes().length + "\r\n");
            bw.write(value + MEMSPLIT_STR);
            bw.write("\r\n");
            bw.flush();
            String line = br.readLine();
            while (line != null) {
                if (line.equals("STORED")) {
                    return true;
                } else if (line.equals("NOT_STORED")) {
                    return set(key, value, expire);
                } else if (line.startsWith("ERROR")) {
                    throw new Exception("MemCache_Result_ERROR");
                } else if (line.startsWith("CLIENT_ERROR")) {
                    throw new Exception("MemCache_Result_CLIENT_ERROR");
                } else if (line.startsWith("SERVER_ERROR ")) {
                    throw new Exception("MemCache_Result_SERVER_ERROR ");
                } else {
                    throw new Exception("MemCache_Result_UNKNOWN_ERROR");
                }
            }
        } catch (Exception ex) {
            throw ex;
        } finally {
            pool.returnObject(poolableSocket);
        }
       throw new Exception("MemCache_Result_NORESPONSE_ERROR");
    }

    public boolean appendDel(String key, String value, long expire) throws Exception {
        key=encodeValue(key);
        value=encodeValue(value);
        return append(key, MEMDELPREFIX_STR + value, expire);
    }

    public boolean delete(String key, long expire) throws Exception {
        key=encodeValue(key);
        if (key == null || key.equals("")) throw new Exception("the key is null,key=" + key);
        PoolableSocket poolableSocket = pool.borrowObject();
        try {
            Socket socket = poolableSocket.getSocket();
            BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            bw.write("delete " + key + " " + expire + "\r\n");
            bw.flush();
            String line = br.readLine();
            while (line != null) {
                if (line.equals("DELETED")) {
                    return true;
                } else if (line.equals("NOT_FOUND") || line.equals("NOT_STORED")) {
                    return false;
                } else if (line.startsWith("ERROR")) {
                    throw new Exception("MemCache_Result_ERROR");
                } else if (line.startsWith("CLIENT_ERROR")) {
                    throw new Exception("MemCache_Result_CLIENT_ERROR");
                } else if (line.startsWith("SERVER_ERROR ")) {
                    throw new Exception("MemCache_Result_SERVER_ERROR ");
                } else {
                    throw new Exception("MemCache_Result_UNKNOWN_ERROR");
                }
            }
        } catch (Exception ex) {
            throw ex;
        } finally {
            pool.returnObject(poolableSocket);
        }
        throw new Exception("MemCache_Result_NORESPONSE_ERROR");
    }

    public boolean cas(String key, String value, long expire) throws Exception {
        key=encodeValue(key);
        value=encodeValue(value);
        if (key == null || key.equals("")) throw new Exception("the key is null,key=" + key);
        if (value == null) throw new Exception("the value is null,value=" + value);
        PoolableSocket poolableSocket = pool.borrowObject();
        try {
            Socket socket = poolableSocket.getSocket();
            BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            bw.write("cas " + key + " 0 " + expire + " " + (value + MEMSPLIT_STR).getBytes().length + "\r\n");
            bw.write(value + MEMSPLIT_STR);
            bw.write("\r\n");
            bw.flush();
            String line = br.readLine();
            while (line != null) {
                System.out.println(line);
                if (line.equals("STORED")) {
                    return true;
                } else if (line.equals("NOT_STORED")) {
                    return false;
                } else if (line.startsWith("ERROR")) {
                    throw new Exception("MemCache_Result_ERROR");
                } else if (line.startsWith("CLIENT_ERROR")) {
                    throw new Exception("MemCache_Result_CLIENT_ERROR");
                } else if (line.startsWith("SERVER_ERROR ")) {
                    throw new Exception("MemCache_Result_SERVER_ERROR ");
                } else {
                    throw new Exception("MemCache_Result_UNKNOWN_ERROR");
                }
            }
        } catch (Exception ex) {
            throw ex;
        } finally {
            pool.returnObject(poolableSocket);
        }
       throw new Exception("MemCache_Result_NORESPONSE_ERROR");
    }

    public boolean flush() throws Exception {
        PoolableSocket poolableSocket = pool.borrowObject();
        try {
            Socket socket = poolableSocket.getSocket();
            BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            bw.write("flush_all\r\n");
            bw.flush();
            String line = br.readLine();
            while (line != null) {
                if (line.equals("OK")) {
                    return true;
                } else if (line.startsWith("ERROR")) {
                    throw new Exception("MemCache_Result_ERROR");
                } else if (line.startsWith("CLIENT_ERROR")) {
                    throw new Exception("MemCache_Result_CLIENT_ERROR");
                } else if (line.startsWith("SERVER_ERROR ")) {
                    throw new Exception("MemCache_Result_SERVER_ERROR ");
                } else {
                    throw new Exception("MemCache_Result_UNKNOWN_ERROR");
                }
            }
        } catch (Exception ex) {
            throw ex;
        } finally {
            pool.returnObject(poolableSocket);
        }
       throw new Exception("MemCache_Result_NORESPONSE_ERROR");
    }

    public String stat() throws Exception {
        String rt = "";
        PoolableSocket poolableSocket = pool.borrowObject();
        try {
            Socket socket = poolableSocket.getSocket();
            BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            bw.write("stats\r\n");
            bw.flush();
            String line = br.readLine();
            while (line != null) {
                if (line.equals("END")) {
                    break;
                } else if (line.startsWith("STAT")) {
                    if (!rt.equals("")) {
                        rt += "\r\n";
                    }
                    rt += line.substring("STAT ".length());
                    line = br.readLine();
                } else if (line.startsWith("ERROR")) {
                    throw new Exception("MemCache_Result_ERROR");
                } else if (line.startsWith("CLIENT_ERROR")) {
                    throw new Exception("MemCache_Result_CLIENT_ERROR");
                } else if (line.startsWith("SERVER_ERROR ")) {
                    throw new Exception("MemCache_Result_SERVER_ERROR ");
                } else {
                    throw new Exception("MemCache_Result_UNKNOWN_ERROR");
                }
            }
        } catch (Exception ex) {
            throw ex;
        } finally {
            pool.returnObject(poolableSocket);
        }
        return rt;
    }

    public static void main(String[] args) throws Exception {
    	
    	
    	System.out.println("MEMSPLIT_STR:" + MEMSPLIT_STR);
    	System.out.println(new MemCacheClient("127.0.0.1", 11211,5,10).set("kinzhang_key","123456",1000000));
    	 System.out.println(new MemCacheClient("127.0.0.1", 11211,5,10).get("kinzhang_key"));
       /* Map<String, String> map = new MemCacheClient("192.168.21.144", 11211).get(new String[]{"黄小平45676"});
        String[] keys = map.keySet().toArray(new String[0]);
        for (int i = 0; i < keys.length; i++) {
            System.out.println(keys[i]);
            System.out.println(map.get(keys[i]));
            System.out.println("================================");
        }*/
        //  System.out.println(new MemCacheClient("192.168.21.144",11211).get("黄小平456"));

        //System.out.println(new MemCacheClient("192.168.21.144", 11211,5,10).cas("黄小平45676", "123321", 100));
        //System.out.println(new MemCacheClient("192.168.21.144", 11211).preappend("黄小平45676", "1111", 0));
       // System.out.println(new MemCacheClient("192.168.21.144", 11211).preappend("黄小平45676", "1111", 0));
       // System.out.println(new MemCacheClient("192.168.21.144", 11211,5,10).get("黄小平45676"));

        // System.out.println(new MemCacheClient().replace("黄小平123","开始加拉斯的接口的方式快乐防盗锁领导方式132132111111111"));
        // System.out.println(new MemCacheClient().set("黄小平123","32213211"));
        // System.out.println(new MemCacheClient().set("黄小平456","322132000011",100));
        //System.out.println(new MemCacheClient().flush());
        // System.out.println(new MemCacheClient().stat());
    }
}
